import { defineComponent, h, isRef, ref, type Component, type ComputedRef, type Ref } from 'vue'
import { useSelector } from '@xstate/vue'
import type { ActorRef, AnyActorRef, UnknownActorLogic } from 'xstate'

type ActorItem = {
  actor: AnyActorRef,
  selector: (selector: (snapshot: any) => unknown) => Ref<unknown>,
  send: (event: {type: string}) => void
}

type Preset = {
  types?: unknown
  components: Record<string, unknown>
  props?: Record<string, (actorDict: Record<string, ActorItem>) => unknown>
  actors?: Record<string, AnyActorRef>
}

type Config = {
  type: string
  children?: Record<string, Config>
  props?: Record<string, string>
}

export function setup(preset: Preset) {
  const {
    actors,
  } = preset

  return {
    createView: (config: Config) => {
      return renderComponent(preset, config)
    }
  }
}

function renderComponent(preset: Preset, config: Config) {
  const {
    type,
    children = {},
    props = {}
  } = config
  const Cmp = preset.components[type] as unknown as Component
  const slots = Object.entries(children).reduce((prev: Record<string, unknown>, [k, v]) => {
    const Child = renderComponent(preset, v)
    prev[k] = () => <Child />
    return prev
  }, {})

  const actorMap = Object.entries(preset.actors ?? {}).reduce((prev, [k, v]) => {
    const item = {
      actor: v,
      selector: (selector: (snapshot: any) => unknown) => useSelector(v, selector),
      send: v.send,
    }
    prev[k] = item
    return prev
  }, {} as Record<string, ActorItem>)

  return defineComponent({
    name: 'Vic',
    setup() {
      
      return () => {

        const _props = Object.entries(props).reduce((prev: Record<string, unknown>, [k, v]) => {
          const prop = preset?.props?.[v]?.(actorMap) ?? v
          // 这样处理才不会丢掉响应式
          prev[k] = isRef(prop) ? prop.value : prop
          return prev
        }, {})

        return h(Cmp, _props, slots)
      }
    }
  })
}
